﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Management;
using System.Globalization;
using System.Threading;

using NxtNet;

namespace NxtNetTest
{
    public partial class frmMain : Form
    {

        // シリアルポートコンボのキーと値をセットで保存。「COM1」が「標準シリアルポート1」等。
        Dictionary<string, string> ComPortTable = new Dictionary<string, string>();    

        // NXTオブジェクト。
        private Nxt nxt;

        /// <summary>NXTの接続状態変数。true=接続中。</summary>
        private bool isConnectedNXT = false;
        /// <summary>NXTに渡すパワー値。</summary>
        private int powerHand, powerArmUpDown, powerArmLeftRight;
        /// <summary>NXTサーボモータ回転角</summary>
        int handDeg, leftDeg, rightDeg;

        /// <summary>NXT制御用スレッド</summary>
        Thread threadNXT;

        /// <summary>スレッド停止用フラグ。</summary>
        private volatile bool _shouldStop;

        public frmMain()
        {
            InitializeComponent();
        }

        /// <summary>
        /// シリアルポートの実装状況を取得。
        /// </summary>
        /// <param name="ComPortTable"></param>
        void ConfigureComPortTable(ref Dictionary<string, string> ComPortTable)
        {
            // シリアルポートの実装状況を取得。
            ManagementClass mcW32SerialPort = new ManagementClass("Win32_SerialPort");
            foreach (ManagementObject aSerialPort in mcW32SerialPort.GetInstances())
            {
                ComPortTable.Add(
                    (string)aSerialPort.GetPropertyValue("Caption"),
                    (string)aSerialPort.GetPropertyValue("DeviceID"));
            }
            string[] ports = SerialPort.GetPortNames();
            foreach (string port in ports)
            {
                if (!ComPortTable.ContainsValue(port))
                {
                    ComPortTable.Add(port, port);
                }
            }
        }

        #region NXTの処理

        private void SetPower()
        {
            _shouldStop = false;

            while (!_shouldStop)
            {
                if (isConnectedNXT)
                {
                    try
                    {
                        // 現在のモータ回転角度を取得。
                        handDeg = nxt.GetOutputState(MotorPort.PortA).TachoCount;
                        leftDeg = nxt.GetOutputState(MotorPort.PortB).TachoCount;
                        rightDeg = nxt.GetOutputState(MotorPort.PortC).TachoCount;

                        // ハンド用モータ制御。
                        if (((-70 >= handDeg) & (powerHand < 0)) | ((10 <= handDeg) & (powerHand > 0)))
                        {
                            // ハンドの開閉を行わない。
                            nxt.SetOutputState(MotorPort.PortA,
                                0,
                                MotorModes.On | MotorModes.Regulated,
                                MotorRegulationMode.Speed,
                                0,
                                MotorRunState.Running,
                                0);
                        }
                        else
                        {
                            // ハンド開閉。
                            nxt.SetOutputState(MotorPort.PortA,
                                (sbyte)powerHand,
                                MotorModes.On | MotorModes.Regulated,
                                MotorRegulationMode.Speed,
                                0,
                                MotorRunState.Running,
                                0);
                        }

                        // アーム上下制御。
                        if (((leftDeg > 2000) & (powerArmUpDown > 0)) | ((leftDeg < -5000) & (powerArmUpDown < 0)))
                        {
                            // アーム上下しない。
                            nxt.SetOutputState(MotorPort.PortB,
                                0,
                                MotorModes.On | MotorModes.Regulated,
                                MotorRegulationMode.Speed,
                                0,
                                MotorRunState.Running,
                                0);
                        }
                        else
                        {
                            // アーム上下。
                            nxt.SetOutputState(MotorPort.PortB,
                                (sbyte)powerArmUpDown,
                                MotorModes.On | MotorModes.Regulated,
                                MotorRegulationMode.Speed,
                                0,
                                MotorRunState.Running,
                                0);
                        }

                        // アーム左右制御。
                        if (((rightDeg > 10000) & (powerArmLeftRight > 0)) | ((rightDeg < -10000) & (powerArmLeftRight < 0)))
                        {
                            // アーム左右しない。
                            nxt.SetOutputState(MotorPort.PortC,
                                0,
                                MotorModes.On | MotorModes.Regulated,
                                MotorRegulationMode.Speed,
                                0,
                                MotorRunState.Running,
                                0);
                        }
                        else
                        {
                            // アーム左右。
                            nxt.SetOutputState(MotorPort.PortC,
                                (sbyte)powerArmLeftRight,
                                MotorModes.On | MotorModes.Regulated,
                                MotorRegulationMode.Speed,
                                0,
                                MotorRunState.Running,
                                0);
                        }

                    }
                    catch
                    {
                    }

                    // NXTと送受信するためのディレイ時間。
                    Thread.Sleep(500);
                }
            }
        }
        #endregion

        /// <summary>
        /// メインフォームロード次のハンドラ。初期化処理を実施。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmMain_Load(object sender, EventArgs e)
        {
            // シリアルポートの実装状況を取得。
            ConfigureComPortTable(ref ComPortTable);

            // 画面の構成。

            // シリアルポートのコンボボックスの内容を構成。
            cmbPortNo.Items.Clear();
            foreach (string aPortCaption in ComPortTable.Keys)
            {
                cmbPortNo.Items.Add(aPortCaption);
            }
            if (ComPortTable.Count > 0)
            {
                cmbPortNo.SelectedIndex = 0;
            }
            else
            {
                MessageBox.Show("シリアルポートが存在しません。");
            }
        }

        /// <summary>
        /// 「接続」ボタンクリックのハンドラ。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                string comPort = ComPortTable[cmbPortNo.SelectedItem.ToString()];
                if (comPort != null)
                {
                    // NXTへ接続。
                    nxt = new Nxt();
                    nxt.Connect(comPort);

                    // NXTを制御するスレッドの開始。
                    threadNXT = new Thread(new ThreadStart(SetPower));
                    threadNXT.IsBackground = true;
                    threadNXT.Start();

                    // 接続管理フラグを「接続」状態に設定。
                    isConnectedNXT = true;

                    // コントロール表示を変更。
                    btnConnect.Enabled = false;
                    btnDisconnect.Enabled = true;
                    
                }
            }
            catch (Exception ex)
            {
                string errMsg = "@btnConnect_Click(): ";
                errMsg += ex.Message;
                Console.WriteLine(errMsg);
                MessageBox.Show(errMsg);
                btnConnect.Enabled = true;
                btnDisconnect.Enabled = false;
            }
        }

        /// <summary>
        /// 「切断」ボタンクリックのハンドラ。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            try
            {
                // NXT切断。
                if (nxt != null)
                {
                    nxt.Disconnect();

                    // 接続管理フラグ更新。
                    isConnectedNXT = false;

                    // コントロール表示を変更。
                    btnConnect.Enabled = true;
                    btnDisconnect.Enabled = false;

                }

            }
            catch (Exception ex)
            {
                string errMsg = ex.Message;
                Console.WriteLine(errMsg);
                MessageBox.Show(errMsg);
            }
        }


        /// <summary>
        /// 「終了」ボタンクリックハンドラ。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnEnd_Click(object sender, EventArgs e)
        {
            // NXTが接続されていたら切断する。
            if (nxt != null)
            {
                nxt.Disconnect();

                // 接続管理フラグ更新。
                isConnectedNXT = false;
            }

            // 終了処理。
            this.Close();
        }

        /// <summary>
        /// Handトラックバースクロールのハンドラ。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void trackBarHand_Scroll(object sender, EventArgs e)
        {
            powerHand = trackBarHand.Value;
            labelHandValue.Text = powerHand.ToString();
        }

        /// <summary>
        /// Arm Up-Down トラックバースクロールハンドラ。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void trackBarArmUpDown_Scroll(object sender, EventArgs e)
        {
            powerArmUpDown = trackBarArmUpDown.Value;
            labelArmUpDownValue.Text = powerArmUpDown.ToString();
        }

        /// <summary>
        /// Arm Left-Right トラックバーのスクロールハンドラ。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void trackBarArmLeftRight_Scroll(object sender, EventArgs e)
        {
            powerArmLeftRight = trackBarArmLeftRight.Value;
            labelArmLeftRightValue.Text = powerArmLeftRight.ToString();
        }
    }
}
